﻿using DevComponents.DotNetBar;
using DevComponents.DotNetBar.Controls;
using Kimd.Common;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace Kimd.Communication
{
    public partial class CommunicationPage : Office2007Form
    {
        #region Properties
        public ICommunicationShow Device;
        public SerializableDictionary<string, string> Cmds { get; set; }
        private CommTypeEnum CommType;
        private bool RevShowSendStrEnable = true;
        private bool RevShowTimeEnable = true;
        private bool RevAutoRowEnable = true;
        private bool RevPauseShowEnable;
        private bool RevHexEnable;
        private bool SendHexEnable;
        private bool CycleSend;
        private int RecvSumCount = 0;
        private int SendSumCount = 0;
        private DataAnaysisHelper AnaysisHelper { get; set; } = new DataAnaysisHelper();
        public Action<ICommunicationShow, SerializableDictionary<string, string>> NotifyCmdChanged;
        #endregion

        #region Constructor
        private CommunicationPage()
        {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
            Cmds = new SerializableDictionary<string, string>();
            PrivateFontCollection pfc = new PrivateFontCollection();
            //if (File.Exists("./fontawesome-webfont.ttf"))
            //{
            //    pfc.AddFontFile("./fontawesome-webfont.ttf");
            //    tTSM_Comm.Font = new Font(pfc.Families[0], 10);
            //}
            this.EnableGlass = false;
        }
        public CommunicationPage(ICommunicationShow device) : this()
        {
            if (device != null)
            {
                Device = device;
                CommTypeEnum comm = CommType = Device.CommType;
                Cmds = device.SendCmd;
            }
        }
        #endregion

        public List<string> GetCMDList()
        {
            List<string> cmdStr = new List<string>();
            foreach (var item in Cmds)
            {
                cmdStr.Add($"{item.Key}:{item.Value}");
            }
            return cmdStr;
        }

        private void CommunicationPage_Load(object sender, EventArgs e)
        {
            bI_Name.Text = Device.GetTreeNodeShowStr();
            Cob_CmdStrAddCMD();
            if (Device != null)
            {
                switch (CommType)
                {
                    case CommTypeEnum.TCPClient:
                        TCPClient client = Device as TCPClient;
                        if (client != null)
                        {
                            client.DatagramReceived -= Client_DatagramReceived;
                            client.DatagramReceived += Client_DatagramReceived;
                            Panel_TCPServer.Visible = false;
                            //splitContainer_Data.Panel1.Controls.Remove(Panel_TCPServer);
                            Panel_TCPServer.Visible = false;
                        }
                        break;
                    case CommTypeEnum.TCPServer:
                        TCPServer server = Device as TCPServer;
                        if (server != null)
                        {
                            server.DatagramReceived -= Server_DatagramReceived;
                            server.DatagramReceived += Server_DatagramReceived;
                            Panel_TCPServer.Visible = true;
                            List<string> clients = server.GetShowList();
                            if (clients.Count > 0)
                                cob_TCpClients.Items.AddRange(clients.ToArray());
                            if(cob_TCpClients.Items.Count > 0)
                                cob_TCpClients.SelectedIndex = 0;
                            Panel_TCPServer.Visible = true;
                        }
                        break;
                    case CommTypeEnum.SerialPort:
                        SerialPortHelper serialPort = Device as SerialPortHelper;
                        if (serialPort != null)
                        {
                            serialPort.DatagramReceived -= SerialPort_DataReceived;
                            serialPort.DatagramReceived += SerialPort_DataReceived;
                            serialPort.CommRunDisplay -= showSerialPortMessage;
                            serialPort.CommRunDisplay += showSerialPortMessage;
                        }
                        break;
                    default:
                        Panel_TCPServer.Visible = false;
                        //splitContainer_Data.Panel1.Controls.Remove(Panel_TCPServer);
                        Panel_TCPServer.Visible = false;
                        break;
                }
                timer1.Enabled = true;
            }
            else
            {
                Panel_TCPServer.Visible = false;
                //splitContainer_Data.Panel1.Controls.Remove(Panel_TCPServer);
                Panel_TCPServer.Visible = false;
            }
            comboBoxEx1.SelectedIndex = 0;
        }

        private void showSerialPortMessage(SerialPortHelper sp, string message)
        {
            if (RevShowSendStrEnable)
            {
                string sendShowStr = $"{(RevShowTimeEnable ? DateTime.Now.ToString("HH:MM:ss-fff ") : "")}{message}{(RevAutoRowEnable ? "\r\n" : "")}";
                ShowRecvMsg(sendShowStr);
            }
        }

        private void SerialPort_DataReceived(object sender, DatagramReceivedEventArgs<byte[]> e)
        {
            //string name = $"{(sender as SerialPortHelper).PortName}";
            ShowRecvString(e.Datagram);
        }

        private void Server_DatagramReceived(object sender, TcpServerDatagramReceivedEventArgs<byte[]> e)
        {
            //string name = $"{(sender as TCPServer).IP}:{(sender as TCPServer).Port}";
            ShowRecvString(e.Datagram);
        }

        private void Client_DatagramReceived(object sender, TcpClientDatagramReceivedEventArgs<byte[]> e)
        {
            //string name = $"{(sender as TCPClient).IP}:{(sender as TCPClient).Port}";
            ShowRecvString(e.Datagram);
            Console.WriteLine($"{DateTime.Now.ToString("HHmmss-fff")}:{e.Datagram}");
        }

        private void ShowRecvString(byte[] e)
        {
            try
            {
                if (!this.Visible || this.IsDisposed)
                    return;
                if (e.Count() <= 0)
                    return;
                int count = e.Length;
                string recvStr = string.Empty;
                string recvShowStr = string.Empty;
                //更新接收数目
                ShowRecvAndSendCount(recvCount: count);
                if (RevHexEnable)
                    recvStr = AnaysisHelper.ByteToHexStr(e, " ");
                else
                    recvStr = Encoding.Default.GetString(e);
                //string fromName = $"[Receive From:{name}]";
                recvShowStr = $"{(RevShowTimeEnable ? DateTime.Now.ToString("HH:MM:ss-fff <---:") :"")}{recvStr}{(RevAutoRowEnable ? "\r\n" : "")}";
                ShowRecvMsg(recvShowStr);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void ShowRecvMsg(string str)
        {
            if (this.IsDisposed||!this.Visible)
                return;
            if (RevPauseShowEnable)
                return;
            if (tb_Recv.InvokeRequired)
                tb_Recv.Invoke(new Action(() =>
                {
                    tb_Recv.AppendText(str);
                }));
            else
                tb_Recv.AppendText(str);
        }
        private void ShowRecvAndSendCount(int recvCount = -1, int sendCount = -1)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action(() =>
                {
                    if (recvCount > -1)
                    {
                        Tss_RecvCount.Text = $"{recvCount}";
                        RecvSumCount = RecvSumCount + recvCount;
                    }
                    if (sendCount > -1)
                    {
                        Tss_SendCount.Text = $"{sendCount}";
                        SendSumCount = SendSumCount + sendCount;
                    }
                }));
            }
            else
            {
                if (recvCount > -1)
                {
                    Tss_RecvCount.Text = $"{recvCount}";
                    RecvSumCount = RecvSumCount + recvCount;
                }
                if (sendCount > -1)
                {
                    Tss_SendCount.Text = $"{sendCount}";
                    SendSumCount = SendSumCount + sendCount;
                }
            }
        }

        private void Cob_CmdStrAddCMD()
        {
            Cob_CmdStr.Items.Clear();
            List<string> Strs = GetCMDList();
            if (Strs.Count > 0) Cob_CmdStr.Items.AddRange(Strs.ToArray());
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (Device != null)
            {
                switch (CommType)
                {
                    case CommTypeEnum.TCPClient:
                        btn_Start.Text = Device.IsOpenFlag ? "关闭" : "打开";
                        btn_Start.Image = Device.IsOpenFlag ? Properties.Resources.Green : Properties.Resources.Red;
                        break;
                    case CommTypeEnum.TCPServer:
                        btn_Start.Text = Device.IsOpenFlag ? "关闭" : "开始监听";
                        btn_Start.Image = Device.IsOpenFlag ? Properties.Resources.Green : Properties.Resources.Red;
                        break;
                    case CommTypeEnum.SerialPort:
                        btn_Start.Text = Device.IsOpenFlag ? "关闭" : "打开";
                        btn_Start.Image = Device.IsOpenFlag ? Properties.Resources.Green : Properties.Resources.Red;
                        break;
                }
            }
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            if (Device != null)
            {
                if (Device.IsOpenFlag)
                    Device.SetOpenOrClose(OpenOrClose.Close);
                else
                    Device.SetOpenOrClose(OpenOrClose.Open);
            }
        }

        private void Cb_SendShowEnable_CheckedChanged(object sender, EventArgs e)
        {
            if (sender is CheckBoxX)
            {
                CheckBoxX cb = sender as CheckBoxX;
                if (cb.Name.Equals(Chb_RevSendShowEnable.Name))
                    RevShowSendStrEnable = Chb_RevSendShowEnable.Checked;
                if (cb.Name.Equals(Chb_RecvHex.Name))
                    RevHexEnable = Chb_RecvHex.Checked;
                if (cb.Name.Equals(Chb_RecvPause.Name))
                {
                    RevPauseShowEnable = Chb_RecvPause.Checked;
                    if (RevPauseShowEnable)
                        tb_Recv.Enabled = false;
                    else
                        tb_Recv.Enabled = true;
                }
                if (cb.Name.Equals(Chb_RecvAddLineReturn.Name))
                    RevAutoRowEnable = Chb_RecvAddLineReturn.Checked;
                if (cb.Name.Equals(Chb_SendAsHex.Name))
                    SendHexEnable = Chb_SendAsHex.Checked;
                if (cb.Name.Equals(cB_Cycle.Name))
                    CycleSend = cB_Cycle.Checked;
            }
        }

        private void linkLabel_SendClear_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            
        }

        private async void btn_Send_Click(object sender, EventArgs e)
        {
            string sendStr = "";
            if (Cob_CmdStr.SelectedIndex == -1)
            {
                sendStr = tb_Send.Text;
                if (comboBoxEx1.SelectedIndex != 0)
                {
                    switch (comboBoxEx1.SelectedItem.ToString())
                    {
                        case "CR/LF":
                            sendStr += "\r\n";
                            break;
                        case "CR":
                            sendStr += "\r";
                            break;
                        case "LF":
                            sendStr += "\n";
                            break;
                    }
                }
            }
            else
            {
                sendStr = Cmds[Cob_CmdStr.SelectedItem.ToString().Split(':')[0]];
            }
            byte[] sendByte = new byte[] { };
            string clientStr = string.Empty;
            if (string.IsNullOrEmpty(sendStr))
                return;
            if (Device == null)
                return;
            if (!Device.IsOpenFlag)
                return;
            List<string> clients = Device.GetShowList();
            if (CommType == CommTypeEnum.TCPServer)
            {
                clientStr = cob_TCpClients.Text;
                if (clients.Count <= 0)
                    return;
                if (cob_TCpClients.SelectedIndex != 0)
                {
                    if (!clients.Contains(clientStr))
                        return;
                }
            }
            int cycleTime = 0;
            if (string.IsNullOrEmpty(tB_CycleTime.Text))
                cycleTime = Convert.ToInt32(tB_CycleTime.WatermarkText);
            else
                cycleTime = Convert.ToInt32(tB_CycleTime.Text);
            await Task.Run(() => 
            {
                if(!CycleSend)
                {
                    try
                    {
                        #region Get String
                        if (SendHexEnable)
                            sendByte = AnaysisHelper.HexStrToByte(sendStr);
                        else
                            sendByte = Encoding.Default.GetBytes(sendStr);
                        #endregion
                        #region send byte
                        bool sendSucceed = false;
                        switch (CommType)
                        {
                            case CommTypeEnum.TCPClient:
                                sendSucceed = (Device as TCPClient).Send(sendByte);
                                break;
                            case CommTypeEnum.TCPServer:
                                TCPServer server = Device as TCPServer;
                                if (clientStr != "AllConnections")
                                {
                                    TcpClientState state = server.TCPClients.First(o => o.EndPoint == clientStr);
                                    if (state != null)
                                        sendSucceed = server.Send(state.TcpClient, sendByte);
                                }
                                else
                                {
                                    sendSucceed = server.SendAll(sendByte);
                                }
                                break;
                            case CommTypeEnum.SerialPort:
                                sendSucceed = (Device as SerialPortHelper).Send(sendByte);
                                break;
                        }
                        #endregion
                        #region show String
                        if (sendSucceed)
                        {
                            if (RevShowSendStrEnable)
                            {
                                string sendShowStr = $"{(RevShowTimeEnable ? DateTime.Now.ToString("HH:MM:ss-fff --->:") : "")}{sendStr}{(RevAutoRowEnable ? "\r\n" : "")}";
                                string sendto = "";
                                ShowRecvMsg(sendShowStr);
                            }
                            ShowRecvAndSendCount(sendCount: sendByte.Count());
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }
                }
                while(CycleSend)
                {
                    try
                    {
                        #region Get String
                        if (SendHexEnable)
                            sendByte = AnaysisHelper.HexStrToByte(sendStr);
                        else
                            sendByte = Encoding.Default.GetBytes(sendStr);
                        #endregion
                        #region send byte
                        bool sendSucceed = false;
                        switch (CommType)
                        {
                            case CommTypeEnum.TCPClient:
                                sendSucceed = (Device as TCPClient).Send(sendByte);
                                break;
                            case CommTypeEnum.TCPServer:
                                TCPServer server = Device as TCPServer;
                                if (clientStr != "AllConnections")
                                {
                                    TcpClientState state = server.TCPClients.First(o => o.EndPoint == clientStr);
                                    if (state != null)
                                        sendSucceed = server.Send(state.TcpClient, sendByte);
                                }
                                else
                                {
                                    sendSucceed = server.SendAll(sendByte);
                                }
                                break;
                            case CommTypeEnum.SerialPort:
                                sendSucceed = (Device as SerialPortHelper).Send(sendByte);
                                break;
                        }
                        #endregion
                        #region show String
                        if (sendSucceed)
                        {
                            if (RevShowSendStrEnable)
                            {
                                string sendShowStr = $"{(RevShowTimeEnable ? DateTime.Now.ToString("HH:MM:ss-fff --->:") : "")}{sendStr}{(RevAutoRowEnable ? "\r\n" : "")}";
                                ShowRecvMsg(sendShowStr);
                            }
                            ShowRecvAndSendCount(sendCount: sendByte.Count());
                        }
                        #endregion
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }
                    Thread.Sleep(cycleTime);
                }
            });
        }

        private void pb_AddPic_Click(object sender, EventArgs e)
        {
            FrmSendCmd frmSendCmd = new FrmSendCmd(Cmds);
            frmSendCmd.ShowDialog();
            if (frmSendCmd.Tag != null)
            {
                Cmds = frmSendCmd.Tag as SerializableDictionary<string, string>;
                Cob_CmdStrAddCMD();
                if (Device != null)
                    Device.SendCmd = Cmds;
                if(NotifyCmdChanged != null)
                    NotifyCmdChanged(Device, Cmds);
            }
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            string str = Cob_CmdStr.Text;
            if (!string.IsNullOrEmpty(str))
            {
                string key = str.Split(new string[] { ":" }, StringSplitOptions.RemoveEmptyEntries)[0];
                if (Cmds.Keys.Contains(key))
                    Cmds.Remove(key);
                Cob_CmdStrAddCMD();
                Cob_CmdStr.SelectedIndex = -1;
                Cob_CmdStr.Text = string.Empty;
                if (Device != null)
                    Device.SendCmd = Cmds;
                if (NotifyCmdChanged != null)
                    NotifyCmdChanged(Device, Cmds);
            }
        }

        private void Tss_Reset_Click(object sender, EventArgs e)
        {
            ShowRecvAndSendCount(0, 0);
            RecvSumCount = 0;
            SendSumCount = 0;
        }

        private void CommunicationPage_FormClosing(object sender, FormClosingEventArgs e)
        {
            timer1.Enabled = false;
            if (Device != null)
            {
                switch (CommType)
                {
                    case CommTypeEnum.TCPClient:
                        TCPClient client = Device as TCPClient;
                        if (client != null)
                        {
                            client.DatagramReceived -= Client_DatagramReceived;
                        }
                        break;
                    case CommTypeEnum.TCPServer:
                        TCPServer server = Device as TCPServer;
                        if (server != null)
                        {
                            server.DatagramReceived -= Server_DatagramReceived;
                        }
                        break;
                    case CommTypeEnum.SerialPort:
                        SerialPortHelper serialPort = Device as SerialPortHelper;
                        if (serialPort != null)
                        {
                            serialPort.DatagramReceived -= SerialPort_DataReceived;
                            serialPort.CommRunDisplay -= showSerialPortMessage;
                        }
                        break;
                }
            }
        }

        private void cob_TCpClients_DropDown(object sender, EventArgs e)
        {
            if (Device != null)
            {
                cob_TCpClients.Items.Clear();
                cob_TCpClients.Items.Add("AllConnections");
                List<string> clients = Device.GetShowList();
                if (clients.Count > 0)
                    cob_TCpClients.Items.AddRange(clients.ToArray());
            }
        }

        private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            contextMenuStrip1.Items.Clear();
            foreach (string item in Cmds.Values)
            {
                ToolStripMenuItem menu = new ToolStripMenuItem(item);
                menu.Text = item;
                menu.Click -= Menu_Click;
                menu.Click += Menu_Click;
                contextMenuStrip1.Items.Add(menu);
            }
        }

        private void Menu_Click(object sender, EventArgs e)
        {
            string value = (sender as ToolStripMenuItem).Text;
            tb_Send.Text = value;
        }

        private void tb_Send_Click(object sender, EventArgs e)
        {
            tb_Send.BackColor = Color.White;
            tb_Send.Border.BorderColor = SystemColors.Highlight;

            tb_Recv.BackColor = Color.WhiteSmoke;
            tb_Recv.Border.BorderColor = Color.FromArgb(200, 200, 200);
        }

        private void tb_Send_MouseEnter(object sender, EventArgs e)
        {
            (sender as TextBoxX).Border.BorderColor = Color.Gray;
        }

        private void tb_Send_MouseLeave(object sender, EventArgs e)
        {
            (sender as TextBoxX).Border.BorderColor = Color.FromArgb(200, 200, 200);
        }

        private void tb_Recv_Click(object sender, EventArgs e)
        {
            tb_Recv.BackColor = Color.White;
            tb_Recv.Border.BorderColor = SystemColors.Highlight;

            tb_Send.BackColor = Color.WhiteSmoke;
            tb_Send.Border.BorderColor = Color.FromArgb(200, 200, 200);
        }

        private void linkLabel_RecvClear_Click(object sender, EventArgs e)
        {
            if (sender is ButtonX)
            {
                ButtonX label = sender as ButtonX;
                if (label.Name.Equals(linkLabel_RecvClear.Name))
                {
                    tb_Recv.Text = "";
                }
                if (label.Name.Equals(linkLabel_SendClear.Name))
                {
                    tb_Send.Text = "";
                }
            }
        }
    }
}
